﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
#if WPF

#else
using Windows.UI.Xaml;
#endif
using Expression = System.Linq.Expressions.Expression;

namespace Eternal.DataVisualization
{
    public static class DataIterator
    {
        internal static string SetDataType(object value)
        {
            if (value is string)
                return "String";
            if (value is DateTime)
                return "DateTime";
            return "Double";
        }

        public static List<object> Iterate(IEnumerable source, string path, ref string type)
        {
            if (String.IsNullOrEmpty(path)) return null;
            var enumerator = source.GetEnumerator();
            if (!enumerator.MoveNext()) return null;
#if WPF
            var propertyInfo = enumerator.Current.GetType().GetProperty(path);
#else
            var propertyInfo = enumerator.Current.GetType().GetRuntimeProperty(path);
#endif
            var fastPropertyInfo = new FastProperty(propertyInfo);
            type = SetDataType(fastPropertyInfo.Get(enumerator.Current));
            var iterator = new List<object>();
            do
            {
                iterator.Add(fastPropertyInfo.Get(enumerator.Current));
            } while (enumerator.MoveNext());
            return iterator;
        }

        public static List<double> IterateDoubleValue(IEnumerable source, string path, ref string type)
        {
            List<double> iterator = null;
            IEnumerator enumerator;
            if (!String.IsNullOrEmpty(path))
            {
                enumerator = source.GetEnumerator();
                if (!enumerator.MoveNext()) return null;
#if WPF
                var propertyInfo = enumerator.Current.GetType().GetProperty(path);
#else
                var propertyInfo = enumerator.Current.GetType().GetRuntimeProperty(path);
#endif
                var fastPropertyInfo = new FastProperty(propertyInfo);
                type = SetDataType(fastPropertyInfo.Get(enumerator.Current));
                iterator = new List<double>();
                do
                {
                    iterator.Add(Convert.ToDouble(fastPropertyInfo.Get(enumerator.Current)));
                } while (enumerator.MoveNext());
            }

            else if (source != null)
            {
                enumerator = source.GetEnumerator();
                if (!enumerator.MoveNext()) return null;
                iterator = new List<double>();
                type = SetDataType(enumerator.Current);
                var i = 0;
                do
                {
                    iterator.Add(Convert.ToDouble(enumerator.Current));
                    iterator.Add(i);
                    i++;
                } while (enumerator.MoveNext());
            }
            return iterator;
        }
    }

    public class FastProperty
    {

        /// <summary>
        /// Gets or sets the property.
        /// </summary>
        /// <value>
        /// The property.
        /// </value>
        public PropertyInfo Property { get; set; }

        /// <summary>
        /// The get delegate
        /// </summary>
        public Func<object, object> GetDelegate;

        /// <summary>
        /// The set delegate
        /// </summary>
        public Action<object, object> SetDelegate;

        /// <summary>
        /// Initializes a new instance of the <see cref="FastProperty"/> class.
        /// </summary>
        /// <param name="property">The property.</param>
        public FastProperty(PropertyInfo property)
        {
            Property = property;
            InitializeGet();
            InitializeSet();
        }

        /// <summary>
        /// Initializes the set.
        /// </summary>
        private void InitializeSet()
        {
            //if (Property == null) return;
            //var instance = Expression.Parameter(typeof(object), "instance");
            //var value = Expression.Parameter(typeof(object), "value");

            //// value as T is slightly faster than (T)value, so if it's not a value type, use that
            //var instanceCast = Property.GetType().GetTypeInfo().IsValueType ? Expression.Convert(instance, Property.DeclaringType) : Expression.TypeAs(instance, Property.DeclaringType);
            //var valueCast = Property.GetType().GetTypeInfo().IsValueType ? Expression.Convert(value, Property.PropertyType) : Expression.TypeAs(value, Property.PropertyType);
            //SetDelegate =
            //    Expression.Lambda<Action<object, object>>(
            //        Expression.Call(instanceCast, Property.GetSetMethod(), valueCast),
            //        new ParameterExpression[] { instance, value }).Compile();
        }

        /// <summary>
        /// Initializes the get.
        /// </summary>
        private void InitializeGet()
        {
            if (Property == null) return;
            var instance = Expression.Parameter(typeof(object), "instance");
#if WPF
            var instanceCast = Property.DeclaringType.IsValueType ? Expression.Convert(instance, Property.DeclaringType) : Expression.TypeAs(instance, Property.DeclaringType);
#else
            var instanceCast = Expression.Convert(instance, Property.DeclaringType);
#endif
            GetDelegate =
                Expression.Lambda<Func<object, object>>(
                    Expression.TypeAs(
                        Expression.Call(instanceCast, Property.GetGetMethod()),
                        typeof(object)), instance).Compile();
        }

        /// <summary>
        /// Gets the specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <returns></returns>
        public object Get(object instance)
        {
            if (GetDelegate != null)
                return GetDelegate(instance);
            return null;
        }

        /// <summary>
        /// Sets the specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="value">The value.</param>
        public void Set(object instance, object value)
        {
            SetDelegate(instance, value);
        }
    }

    public static class Extension
    {
#if NETFX_CORE

        private static DateTime BaseDate = new DateTime(1899, 12, 30);

        public static double ToOADate(this DateTime time)
        {
            return time.Subtract(BaseDate).TotalDays;
        }

        internal static MethodInfo GetSetMethod(this PropertyInfo propertyInfo)
        {
            return propertyInfo.SetMethod;
        }

        internal static MethodInfo GetGetMethod(this PropertyInfo propertyInfo)
        {
            return propertyInfo.GetMethod;
        }

#endif
    }

    internal static class DataResource
    {
        internal static ResourceDictionary GetResource = new ResourceDictionary
        {
#if WPF
            Source = new Uri(@"/Eternal.DataVisualization.WPF;component/Series/Resource.xaml", UriKind.Relative)
           
     
#else
        Source = new Uri(@"ms-appx:///Eternal.DataVisualization.WPF/Themes/Resource.xaml")
#endif

        };

        internal static ResourceDictionary GetLegendResource = new ResourceDictionary
        {
#if WPF
            Source = new Uri(@"/Eternal.DataVisualization.WPF;component/Legend.xaml", UriKind.Relative)
#else
            Source = new Uri(@"ms-appx:///Eternal.DataVisualization.WPF/Legend.xaml")
#endif
        };
    }
}